/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.solr.client.ref_guide_examples;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.SolrQuery;
import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.util.ExternalPaths;
import org.junit.After;
import org.junit.BeforeClass;

/**
 * Example SolrJ usage for indexing nested documents
 *
 * <p>Snippets surrounded by "tag" and "end" comments are extracted and used in the Solr Reference
 * Guide.
 */
public class IndexingNestedDocuments extends SolrCloudTestCase {
  public static final String ANON_KIDS_CONFIG = "anon_kids_configset";

  // This is the client that we configure to use in the example docs.
  private static SolrClient clientUsedInSolrJExample;

  @BeforeClass
  public static void setupCluster() throws Exception {
    configureCluster(1)
        // when indexing 'anonymous' kids, we need a schema that doesn't use _nest_path_ so
        // that we can use [child] transformer with a parentFilter...
        .addConfig(ANON_KIDS_CONFIG, ExternalPaths.TECHPRODUCTS_CONFIGSET)
        .configure();
  }

  @After
  public void cleanCollections() throws Exception {
    cluster.deleteAllCollections();
  }

  /** Returns the already configured client for a specific example. */
  public static SolrClient getSolrClient() {
    return clientUsedInSolrJExample;
  }

  /**
   * Demo of using anonymous children when indexing hierarchical documents. This test code is used
   * as an 'include' from the ref-guide
   */
  public void testIndexingAnonKids() throws Exception {
    final String collection = "test_anon";

    CollectionAdminRequest.createCollection(collection, ANON_KIDS_CONFIG, 1, 1)
        .process(cluster.getSolrClient());

    // configure the client with the default collection name, to simplify our example below.
    IndexingNestedDocuments.clientUsedInSolrJExample =
        cluster.basicSolrClientBuilder().withDefaultCollection(collection).build();

    //
    // DO NOT MODIFY THESE EXAMPLE DOCS WITHOUT MAKING THE SAME CHANGES TO THE JSON AND XML
    // EQUIVALENT EXAMPLES IN 'indexing-nested-documents.adoc'
    //

    // tag::anon-kids[]
    try (SolrClient client = getSolrClient()) {

      final SolrInputDocument p1 = new SolrInputDocument();
      p1.setField("id", "P11!prod");
      p1.setField("type_s", "PRODUCT");
      p1.setField("name_s", "Swingline Stapler");
      p1.setField("description_t", "The Cadillac of office staplers ...");
      {
        final SolrInputDocument s1 = new SolrInputDocument();
        s1.setField("id", "P11!S21");
        s1.setField("type_s", "SKU");
        s1.setField("color_s", "RED");
        s1.setField("price_i", 42);
        {
          final SolrInputDocument m1 = new SolrInputDocument();
          m1.setField("id", "P11!D41");
          m1.setField("type_s", "MANUAL");
          m1.setField("name_s", "Red Swingline Brochure");
          m1.setField("pages_i", 1);
          m1.setField("content_t", "...");

          s1.addChildDocument(m1);
        }

        final SolrInputDocument s2 = new SolrInputDocument();
        s2.setField("id", "P11!S31");
        s2.setField("type_s", "SKU");
        s2.setField("color_s", "BLACK");
        s2.setField("price_i", 3);

        final SolrInputDocument m1 = new SolrInputDocument();
        m1.setField("id", "P11!D51");
        m1.setField("type_s", "MANUAL");
        m1.setField("name_s", "Quick Reference Guide");
        m1.setField("pages_i", 1);
        m1.setField("content_t", "How to use your stapler ...");

        final SolrInputDocument m2 = new SolrInputDocument();
        m2.setField("id", "P11!D61");
        m2.setField("type_s", "MANUAL");
        m2.setField("name_s", "Warranty Details");
        m2.setField("pages_i", 42);
        m2.setField("content_t", "... lifetime guarantee ...");

        p1.addChildDocuments(Arrays.asList(s1, s2, m1, m2));
      }

      client.add(p1);
      // end::anon-kids[]

      client.commit();

      final SolrDocumentList docs =
          getSolrClient()
              .query(
                  new SolrQuery("description_t:Cadillac")
                      .set("fl", "*,[child parentFilter='type_s:PRODUCT']"))
              .getResults();

      assertEquals(1, docs.getNumFound());
      assertEquals("P11!prod", docs.get(0).getFieldValue("id"));

      // [child] returns a flat list of all (anon) descendents
      assertEquals(5, docs.get(0).getChildDocumentCount());
      assertEquals(5, docs.get(0).getChildDocuments().size());

      // flat list is depth first...
      final SolrDocument red_stapler_brochure = docs.get(0).getChildDocuments().get(0);
      assertEquals("P11!D41", red_stapler_brochure.getFieldValue("id"));

      final SolrDocument red_stapler = docs.get(0).getChildDocuments().get(1);
      assertEquals("P11!S21", red_stapler.getFieldValue("id"));
    }
  }

  /**
   * Demo of using <code>NestPath</code> related pseudo-fields when indexing hierarchical documents.
   * This test code is used as an 'include' from the ref-guide
   */
  public void testIndexingUsingNestPath() throws Exception {
    final String collection = "test_anon";
    CollectionAdminRequest.createCollection(collection, 1, 1).process(cluster.getSolrClient());

    // configure the client with the default collection name, to simplify our example below.
    IndexingNestedDocuments.clientUsedInSolrJExample =
        cluster.basicSolrClientBuilder().withDefaultCollection(collection).build();

    //
    // DO NOT MODIFY THESE EXAMPLE DOCS WITHOUT MAKING THE SAME CHANGES TO THE JSON AND XML
    // EQUIVALENT EXAMPLES IN 'indexing-nested-documents.adoc'
    //

    // tag::nest-path[]
    try (SolrClient client = getSolrClient()) {

      final SolrInputDocument p1 = new SolrInputDocument();
      p1.setField("id", "P11!prod");
      p1.setField("name_s", "Swingline Stapler");
      p1.setField("description_t", "The Cadillac of office staplers ...");
      {
        final SolrInputDocument s1 = new SolrInputDocument();
        s1.setField("id", "P11!S21");
        s1.setField("color_s", "RED");
        s1.setField("price_i", 42);
        {
          final SolrInputDocument m1 = new SolrInputDocument();
          m1.setField("id", "P11!D41");
          m1.setField("name_s", "Red Swingline Brochure");
          m1.setField("pages_i", 1);
          m1.setField("content_t", "...");

          s1.setField("manuals", m1);
        }

        final SolrInputDocument s2 = new SolrInputDocument();
        s2.setField("id", "P11!S31");
        s2.setField("color_s", "BLACK");
        s2.setField("price_i", 3);

        p1.setField("skus", Arrays.asList(s1, s2));
      }
      {
        final SolrInputDocument m1 = new SolrInputDocument();
        m1.setField("id", "P11!D51");
        m1.setField("name_s", "Quick Reference Guide");
        m1.setField("pages_i", 1);
        m1.setField("content_t", "How to use your stapler ...");

        final SolrInputDocument m2 = new SolrInputDocument();
        m2.setField("id", "P11!D61");
        m2.setField("name_s", "Warranty Details");
        m2.setField("pages_i", 42);
        m2.setField("content_t", "... lifetime guarantee ...");

        p1.setField("manuals", Arrays.asList(m1, m2));
      }

      final SolrInputDocument p2 = new SolrInputDocument();
      p2.setField("id", "P22!prod");
      p2.setField("name_s", "Mont Blanc Fountain Pen");
      p2.setField("description_t", "A Premium Writing Instrument ...");
      {
        final SolrInputDocument s1 = new SolrInputDocument();
        s1.setField("id", "P22!S22");
        s1.setField("color_s", "RED");
        s1.setField("price_i", 89);
        {
          final SolrInputDocument m1 = new SolrInputDocument();
          m1.setField("id", "P22!D42");
          m1.setField("name_s", "Red Mont Blanc Brochure");
          m1.setField("pages_i", 1);
          m1.setField("content_t", "...");

          s1.setField("manuals", m1);
        }

        final SolrInputDocument s2 = new SolrInputDocument();
        s2.setField("id", "P22!S32");
        s2.setField("color_s", "BLACK");
        s2.setField("price_i", 67);

        p2.setField("skus", Arrays.asList(s1, s2));
      }
      {
        final SolrInputDocument m1 = new SolrInputDocument();
        m1.setField("id", "P22!D52");
        m1.setField("name_s", "How To Use A Pen");
        m1.setField("pages_i", 42);
        m1.setField("content_t", "Start by removing the cap ...");

        p2.setField("manuals", m1);
      }

      client.add(Arrays.asList(p1, p2));
      // end::nest-path[]

      client.commit();

      // Now a quick sanity check that the nest path is working properly...

      final SolrDocumentList docs =
          getSolrClient()
              .query(new SolrQuery("description_t:Writing").set("fl", "*,[child]"))
              .getResults();

      assertEquals(1, docs.getNumFound());
      assertEquals("P22!prod", docs.get(0).getFieldValue("id"));

      assertEquals(1, docs.get(0).getFieldValues("manuals").size());
      assertEquals(
          SolrDocument.class, docs.get(0).getFieldValues("manuals").iterator().next().getClass());

      assertEquals(2, docs.get(0).getFieldValues("skus").size());
      final List<Object> skus = new ArrayList<>(docs.get(0).getFieldValues("skus"));

      assertEquals(SolrDocument.class, skus.get(0).getClass());
      assertEquals(SolrDocument.class, skus.get(1).getClass());

      final SolrDocument red_pen = (SolrDocument) skus.get(0);
      assertEquals("P22!S22", red_pen.getFieldValue("id"));

      assertEquals(1, red_pen.getFieldValues("manuals").size());
      assertEquals(
          SolrDocument.class, red_pen.getFieldValues("manuals").iterator().next().getClass());

      final SolrDocument red_pen_brochure =
          (SolrDocument) red_pen.getFieldValues("manuals").iterator().next();
      assertEquals("P22!D42", red_pen_brochure.getFieldValue("id"));
    }
  }
}
